<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>协程间的信道 | 卡夫卡的岛上书店</title>
    <meta name="generator" content="VuePress 1.7.1">
    <link rel="icon" href="/blogs/blogs/favicon.ico">
    <meta name="description" content="卡夫卡的岛上书店是凯小默的个人博客，用于记录学习笔记、分享音乐、书籍、旅行等个人兴趣的站点。">
    <meta name="keywords" content="凯小默,凯小默的博客,博客,个人博客,vue,vuejs,vuepress,vuepress-theme-reco,卡夫卡,岛上书店,卡夫卡的岛上书店,音乐,电影,书籍">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
    
    <link rel="preload" href="/blogs/assets/css/0.styles.7a0dfb7e.css" as="style"><link rel="preload" href="/blogs/assets/js/app.b6577c57.js" as="script"><link rel="preload" href="/blogs/assets/js/2.44f167e6.js" as="script"><link rel="preload" href="/blogs/assets/js/17.59a993c4.js" as="script"><link rel="preload" href="/blogs/assets/js/10.3a18c1d1.js" as="script"><link rel="prefetch" href="/blogs/assets/js/100.61348934.js"><link rel="prefetch" href="/blogs/assets/js/101.42e6df4a.js"><link rel="prefetch" href="/blogs/assets/js/102.6a2750c2.js"><link rel="prefetch" href="/blogs/assets/js/103.0ff299f9.js"><link rel="prefetch" href="/blogs/assets/js/104.3b570960.js"><link rel="prefetch" href="/blogs/assets/js/105.37d6cfdf.js"><link rel="prefetch" href="/blogs/assets/js/106.1dfdd5aa.js"><link rel="prefetch" href="/blogs/assets/js/107.27a87e39.js"><link rel="prefetch" href="/blogs/assets/js/108.36db6d97.js"><link rel="prefetch" href="/blogs/assets/js/109.bd3f60c5.js"><link rel="prefetch" href="/blogs/assets/js/11.82fbfbb6.js"><link rel="prefetch" href="/blogs/assets/js/110.5633993c.js"><link rel="prefetch" href="/blogs/assets/js/111.4557a9aa.js"><link rel="prefetch" href="/blogs/assets/js/112.f71fc74e.js"><link rel="prefetch" href="/blogs/assets/js/113.d999e553.js"><link rel="prefetch" href="/blogs/assets/js/114.4410179e.js"><link rel="prefetch" href="/blogs/assets/js/115.1e9773e3.js"><link rel="prefetch" href="/blogs/assets/js/116.212695ab.js"><link rel="prefetch" href="/blogs/assets/js/117.006d9038.js"><link rel="prefetch" href="/blogs/assets/js/118.b47f7110.js"><link rel="prefetch" href="/blogs/assets/js/119.286ab222.js"><link rel="prefetch" href="/blogs/assets/js/12.8b07f727.js"><link rel="prefetch" href="/blogs/assets/js/120.90205ba7.js"><link rel="prefetch" href="/blogs/assets/js/121.5beb3d8c.js"><link rel="prefetch" href="/blogs/assets/js/122.7ab018ab.js"><link rel="prefetch" href="/blogs/assets/js/123.843ac3bb.js"><link rel="prefetch" href="/blogs/assets/js/124.f04c46cb.js"><link rel="prefetch" href="/blogs/assets/js/125.6d88bedc.js"><link rel="prefetch" href="/blogs/assets/js/126.5bfac816.js"><link rel="prefetch" href="/blogs/assets/js/127.2a7e4b14.js"><link rel="prefetch" href="/blogs/assets/js/128.59661175.js"><link rel="prefetch" href="/blogs/assets/js/129.36dade94.js"><link rel="prefetch" href="/blogs/assets/js/13.6b3d22fb.js"><link rel="prefetch" href="/blogs/assets/js/130.f61ee012.js"><link rel="prefetch" href="/blogs/assets/js/131.5ed7262d.js"><link rel="prefetch" href="/blogs/assets/js/132.9bf07050.js"><link rel="prefetch" href="/blogs/assets/js/133.cd691efa.js"><link rel="prefetch" href="/blogs/assets/js/134.d4b30918.js"><link rel="prefetch" href="/blogs/assets/js/135.0889a1e6.js"><link rel="prefetch" href="/blogs/assets/js/136.69cb5efa.js"><link rel="prefetch" href="/blogs/assets/js/137.0d5860ba.js"><link rel="prefetch" href="/blogs/assets/js/138.0ddd14b5.js"><link rel="prefetch" href="/blogs/assets/js/139.042053c1.js"><link rel="prefetch" href="/blogs/assets/js/14.5d4e7a64.js"><link rel="prefetch" href="/blogs/assets/js/140.58984eaa.js"><link rel="prefetch" href="/blogs/assets/js/141.e517a3c5.js"><link rel="prefetch" href="/blogs/assets/js/142.3ac7fced.js"><link rel="prefetch" href="/blogs/assets/js/143.2732c57b.js"><link rel="prefetch" href="/blogs/assets/js/144.c88252d1.js"><link rel="prefetch" href="/blogs/assets/js/145.ad5589ae.js"><link rel="prefetch" href="/blogs/assets/js/146.40250fde.js"><link rel="prefetch" href="/blogs/assets/js/147.4fa20f68.js"><link rel="prefetch" href="/blogs/assets/js/148.efce1d32.js"><link rel="prefetch" href="/blogs/assets/js/149.f8b4e8e9.js"><link rel="prefetch" href="/blogs/assets/js/15.dd270c50.js"><link rel="prefetch" href="/blogs/assets/js/150.5e2c993b.js"><link rel="prefetch" href="/blogs/assets/js/151.26e2eb05.js"><link rel="prefetch" href="/blogs/assets/js/152.466c45c9.js"><link rel="prefetch" href="/blogs/assets/js/153.fb4bc7db.js"><link rel="prefetch" href="/blogs/assets/js/154.1581847e.js"><link rel="prefetch" href="/blogs/assets/js/155.598b4be9.js"><link rel="prefetch" href="/blogs/assets/js/156.348bd2b2.js"><link rel="prefetch" href="/blogs/assets/js/157.f621e49c.js"><link rel="prefetch" href="/blogs/assets/js/158.2b5f3013.js"><link rel="prefetch" href="/blogs/assets/js/159.6214913e.js"><link rel="prefetch" href="/blogs/assets/js/16.89c35137.js"><link rel="prefetch" href="/blogs/assets/js/160.00f7d0b6.js"><link rel="prefetch" href="/blogs/assets/js/161.04fe0063.js"><link rel="prefetch" href="/blogs/assets/js/162.2f60c4fc.js"><link rel="prefetch" href="/blogs/assets/js/163.d24d684b.js"><link rel="prefetch" href="/blogs/assets/js/164.a22c8ce6.js"><link rel="prefetch" href="/blogs/assets/js/165.29df00c2.js"><link rel="prefetch" href="/blogs/assets/js/166.266719f5.js"><link rel="prefetch" href="/blogs/assets/js/167.6fd7fee8.js"><link rel="prefetch" href="/blogs/assets/js/168.a172abbc.js"><link rel="prefetch" href="/blogs/assets/js/169.198b869c.js"><link rel="prefetch" href="/blogs/assets/js/170.998b2f15.js"><link rel="prefetch" href="/blogs/assets/js/171.92c054fa.js"><link rel="prefetch" href="/blogs/assets/js/172.731e2473.js"><link rel="prefetch" href="/blogs/assets/js/173.ba3ff148.js"><link rel="prefetch" href="/blogs/assets/js/174.2ae915d3.js"><link rel="prefetch" href="/blogs/assets/js/175.dfc3341b.js"><link rel="prefetch" href="/blogs/assets/js/176.81677471.js"><link rel="prefetch" href="/blogs/assets/js/177.814d7c87.js"><link rel="prefetch" href="/blogs/assets/js/178.24b1b2df.js"><link rel="prefetch" href="/blogs/assets/js/179.95268dfb.js"><link rel="prefetch" href="/blogs/assets/js/18.3c7337d8.js"><link rel="prefetch" href="/blogs/assets/js/180.462b461f.js"><link rel="prefetch" href="/blogs/assets/js/181.1ee71c3d.js"><link rel="prefetch" href="/blogs/assets/js/182.78063d7a.js"><link rel="prefetch" href="/blogs/assets/js/183.a19bc5ff.js"><link rel="prefetch" href="/blogs/assets/js/184.7360e730.js"><link rel="prefetch" href="/blogs/assets/js/185.91647c2b.js"><link rel="prefetch" href="/blogs/assets/js/186.b4d5fbd8.js"><link rel="prefetch" href="/blogs/assets/js/187.cea7dd6e.js"><link rel="prefetch" href="/blogs/assets/js/188.0f474d25.js"><link rel="prefetch" href="/blogs/assets/js/189.9f1ea486.js"><link rel="prefetch" href="/blogs/assets/js/19.27625bd6.js"><link rel="prefetch" href="/blogs/assets/js/190.741abdcd.js"><link rel="prefetch" href="/blogs/assets/js/191.fb951065.js"><link rel="prefetch" href="/blogs/assets/js/192.992c1657.js"><link rel="prefetch" href="/blogs/assets/js/193.13763adc.js"><link rel="prefetch" href="/blogs/assets/js/194.0e541d84.js"><link rel="prefetch" href="/blogs/assets/js/195.56068487.js"><link rel="prefetch" href="/blogs/assets/js/196.2b45f3a3.js"><link rel="prefetch" href="/blogs/assets/js/197.dbdaf478.js"><link rel="prefetch" href="/blogs/assets/js/198.4d38424f.js"><link rel="prefetch" href="/blogs/assets/js/199.60402a1e.js"><link rel="prefetch" href="/blogs/assets/js/20.6a1cd4b0.js"><link rel="prefetch" href="/blogs/assets/js/200.022d44fb.js"><link rel="prefetch" href="/blogs/assets/js/201.f709417f.js"><link rel="prefetch" href="/blogs/assets/js/202.fcf42b43.js"><link rel="prefetch" href="/blogs/assets/js/203.46ea886e.js"><link rel="prefetch" href="/blogs/assets/js/204.fb70e5a5.js"><link rel="prefetch" href="/blogs/assets/js/205.c81e62f5.js"><link rel="prefetch" href="/blogs/assets/js/206.55e7bb6c.js"><link rel="prefetch" href="/blogs/assets/js/207.4cd290a8.js"><link rel="prefetch" href="/blogs/assets/js/208.23b65267.js"><link rel="prefetch" href="/blogs/assets/js/209.ba939372.js"><link rel="prefetch" href="/blogs/assets/js/21.ece1fdce.js"><link rel="prefetch" href="/blogs/assets/js/210.4de738a7.js"><link rel="prefetch" href="/blogs/assets/js/211.4b462ed7.js"><link rel="prefetch" href="/blogs/assets/js/212.14ec6854.js"><link rel="prefetch" href="/blogs/assets/js/213.e6f24ac6.js"><link rel="prefetch" href="/blogs/assets/js/214.3489ed46.js"><link rel="prefetch" href="/blogs/assets/js/215.bfbf9fb7.js"><link rel="prefetch" href="/blogs/assets/js/216.4c4f8976.js"><link rel="prefetch" href="/blogs/assets/js/217.ebc4029f.js"><link rel="prefetch" href="/blogs/assets/js/218.5e55974b.js"><link rel="prefetch" href="/blogs/assets/js/219.73beef2d.js"><link rel="prefetch" href="/blogs/assets/js/22.b69b7288.js"><link rel="prefetch" href="/blogs/assets/js/220.1094b5c6.js"><link rel="prefetch" href="/blogs/assets/js/221.d83bbdbc.js"><link rel="prefetch" href="/blogs/assets/js/222.00644dba.js"><link rel="prefetch" href="/blogs/assets/js/223.31d8fcdf.js"><link rel="prefetch" href="/blogs/assets/js/224.aef72f74.js"><link rel="prefetch" href="/blogs/assets/js/225.1c23f9c7.js"><link rel="prefetch" href="/blogs/assets/js/226.2a24ccd8.js"><link rel="prefetch" href="/blogs/assets/js/227.61b776c4.js"><link rel="prefetch" href="/blogs/assets/js/228.cccd80f8.js"><link rel="prefetch" href="/blogs/assets/js/229.3264b446.js"><link rel="prefetch" href="/blogs/assets/js/23.ff379a76.js"><link rel="prefetch" href="/blogs/assets/js/230.734e70e1.js"><link rel="prefetch" href="/blogs/assets/js/231.387b34d7.js"><link rel="prefetch" href="/blogs/assets/js/232.01029451.js"><link rel="prefetch" href="/blogs/assets/js/233.d970f564.js"><link rel="prefetch" href="/blogs/assets/js/234.9a274128.js"><link rel="prefetch" href="/blogs/assets/js/235.e9655c87.js"><link rel="prefetch" href="/blogs/assets/js/236.dd33b8e8.js"><link rel="prefetch" href="/blogs/assets/js/237.a762a9b9.js"><link rel="prefetch" href="/blogs/assets/js/238.4f341fc8.js"><link rel="prefetch" href="/blogs/assets/js/239.445f4a71.js"><link rel="prefetch" href="/blogs/assets/js/24.d6f90646.js"><link rel="prefetch" href="/blogs/assets/js/240.8b6ca172.js"><link rel="prefetch" href="/blogs/assets/js/241.fc0fbb3a.js"><link rel="prefetch" href="/blogs/assets/js/242.121b560f.js"><link rel="prefetch" href="/blogs/assets/js/243.65bbc30d.js"><link rel="prefetch" href="/blogs/assets/js/244.4e1a4f6f.js"><link rel="prefetch" href="/blogs/assets/js/245.74650efa.js"><link rel="prefetch" href="/blogs/assets/js/246.44410da9.js"><link rel="prefetch" href="/blogs/assets/js/247.8b17ab29.js"><link rel="prefetch" href="/blogs/assets/js/248.bfd3427b.js"><link rel="prefetch" href="/blogs/assets/js/249.657d85b5.js"><link rel="prefetch" href="/blogs/assets/js/25.33d1b037.js"><link rel="prefetch" href="/blogs/assets/js/250.85d9364b.js"><link rel="prefetch" href="/blogs/assets/js/251.3a2731a1.js"><link rel="prefetch" href="/blogs/assets/js/252.614dddaa.js"><link rel="prefetch" href="/blogs/assets/js/253.e9644a72.js"><link rel="prefetch" href="/blogs/assets/js/254.91d89e86.js"><link rel="prefetch" href="/blogs/assets/js/255.3e45efd3.js"><link rel="prefetch" href="/blogs/assets/js/256.e3855631.js"><link rel="prefetch" href="/blogs/assets/js/257.c99ba8b4.js"><link rel="prefetch" href="/blogs/assets/js/258.d8b477e4.js"><link rel="prefetch" href="/blogs/assets/js/259.2ceb56a0.js"><link rel="prefetch" href="/blogs/assets/js/26.640d9a68.js"><link rel="prefetch" href="/blogs/assets/js/260.b4d81456.js"><link rel="prefetch" href="/blogs/assets/js/261.5f97ec93.js"><link rel="prefetch" href="/blogs/assets/js/262.65521c65.js"><link rel="prefetch" href="/blogs/assets/js/263.0b120aee.js"><link rel="prefetch" href="/blogs/assets/js/264.cb6dba1b.js"><link rel="prefetch" href="/blogs/assets/js/265.7a6b03f8.js"><link rel="prefetch" href="/blogs/assets/js/266.b5c6fa8d.js"><link rel="prefetch" href="/blogs/assets/js/267.be1a936e.js"><link rel="prefetch" href="/blogs/assets/js/268.1e24e2cc.js"><link rel="prefetch" href="/blogs/assets/js/269.0816f7a1.js"><link rel="prefetch" href="/blogs/assets/js/27.de6079ae.js"><link rel="prefetch" href="/blogs/assets/js/270.5d863231.js"><link rel="prefetch" href="/blogs/assets/js/271.6bf6c906.js"><link rel="prefetch" href="/blogs/assets/js/272.487d4e56.js"><link rel="prefetch" href="/blogs/assets/js/273.e851831e.js"><link rel="prefetch" href="/blogs/assets/js/274.ddfce773.js"><link rel="prefetch" href="/blogs/assets/js/275.7be6ccd7.js"><link rel="prefetch" href="/blogs/assets/js/276.e114860f.js"><link rel="prefetch" href="/blogs/assets/js/277.d426622a.js"><link rel="prefetch" href="/blogs/assets/js/278.065ed690.js"><link rel="prefetch" href="/blogs/assets/js/279.a97eb279.js"><link rel="prefetch" href="/blogs/assets/js/28.81d3531c.js"><link rel="prefetch" href="/blogs/assets/js/280.afca3fe3.js"><link rel="prefetch" href="/blogs/assets/js/281.e8afc801.js"><link rel="prefetch" href="/blogs/assets/js/282.6b369e76.js"><link rel="prefetch" href="/blogs/assets/js/283.b4e68cf0.js"><link rel="prefetch" href="/blogs/assets/js/284.4cc56fa2.js"><link rel="prefetch" href="/blogs/assets/js/285.37f3a771.js"><link rel="prefetch" href="/blogs/assets/js/286.eb75f6fc.js"><link rel="prefetch" href="/blogs/assets/js/287.5044aae8.js"><link rel="prefetch" href="/blogs/assets/js/288.3d6057f4.js"><link rel="prefetch" href="/blogs/assets/js/289.49f89a04.js"><link rel="prefetch" href="/blogs/assets/js/29.7978b908.js"><link rel="prefetch" href="/blogs/assets/js/290.3a24c2c7.js"><link rel="prefetch" href="/blogs/assets/js/291.a7a9f8d2.js"><link rel="prefetch" href="/blogs/assets/js/292.e6641495.js"><link rel="prefetch" href="/blogs/assets/js/293.69637ebc.js"><link rel="prefetch" href="/blogs/assets/js/294.34446dd9.js"><link rel="prefetch" href="/blogs/assets/js/295.e697cc4f.js"><link rel="prefetch" href="/blogs/assets/js/296.3a5217af.js"><link rel="prefetch" href="/blogs/assets/js/297.89f3f217.js"><link rel="prefetch" href="/blogs/assets/js/298.7d1e4493.js"><link rel="prefetch" href="/blogs/assets/js/299.dced7d34.js"><link rel="prefetch" href="/blogs/assets/js/3.469820f3.js"><link rel="prefetch" href="/blogs/assets/js/30.a3caea8a.js"><link rel="prefetch" href="/blogs/assets/js/300.c089a9a0.js"><link rel="prefetch" href="/blogs/assets/js/301.0a5a4fb3.js"><link rel="prefetch" href="/blogs/assets/js/302.24f71a08.js"><link rel="prefetch" href="/blogs/assets/js/303.e353ec2a.js"><link rel="prefetch" href="/blogs/assets/js/304.80727d70.js"><link rel="prefetch" href="/blogs/assets/js/305.5a5bc564.js"><link rel="prefetch" href="/blogs/assets/js/306.e7f9c236.js"><link rel="prefetch" href="/blogs/assets/js/307.08effa0e.js"><link rel="prefetch" href="/blogs/assets/js/308.4112802b.js"><link rel="prefetch" href="/blogs/assets/js/309.9571f2ca.js"><link rel="prefetch" href="/blogs/assets/js/31.5710f44b.js"><link rel="prefetch" href="/blogs/assets/js/310.5d368a48.js"><link rel="prefetch" href="/blogs/assets/js/311.16c11adf.js"><link rel="prefetch" href="/blogs/assets/js/312.db502820.js"><link rel="prefetch" href="/blogs/assets/js/313.325b6dba.js"><link rel="prefetch" href="/blogs/assets/js/314.16ea2951.js"><link rel="prefetch" href="/blogs/assets/js/315.6bb51af6.js"><link rel="prefetch" href="/blogs/assets/js/316.48dbfc7f.js"><link rel="prefetch" href="/blogs/assets/js/317.196398a0.js"><link rel="prefetch" href="/blogs/assets/js/318.4e8695f4.js"><link rel="prefetch" href="/blogs/assets/js/319.d5b8f8df.js"><link rel="prefetch" href="/blogs/assets/js/32.e12ad4a6.js"><link rel="prefetch" href="/blogs/assets/js/320.dbd4d64d.js"><link rel="prefetch" href="/blogs/assets/js/321.929a6292.js"><link rel="prefetch" href="/blogs/assets/js/322.0e3a130d.js"><link rel="prefetch" href="/blogs/assets/js/323.7d8c6df6.js"><link rel="prefetch" href="/blogs/assets/js/324.755c1e37.js"><link rel="prefetch" href="/blogs/assets/js/325.16d3b2aa.js"><link rel="prefetch" href="/blogs/assets/js/326.f9ea6d63.js"><link rel="prefetch" href="/blogs/assets/js/327.7aea27e4.js"><link rel="prefetch" href="/blogs/assets/js/328.4eea35b9.js"><link rel="prefetch" href="/blogs/assets/js/329.4f5ea131.js"><link rel="prefetch" href="/blogs/assets/js/33.15722ed9.js"><link rel="prefetch" href="/blogs/assets/js/330.5e79eb19.js"><link rel="prefetch" href="/blogs/assets/js/331.c73daf72.js"><link rel="prefetch" href="/blogs/assets/js/332.bf5b7eb2.js"><link rel="prefetch" href="/blogs/assets/js/333.bf517f24.js"><link rel="prefetch" href="/blogs/assets/js/334.2e504c7c.js"><link rel="prefetch" href="/blogs/assets/js/335.bbbe2c4d.js"><link rel="prefetch" href="/blogs/assets/js/336.4a98dd79.js"><link rel="prefetch" href="/blogs/assets/js/337.56b27c83.js"><link rel="prefetch" href="/blogs/assets/js/338.eeade5e8.js"><link rel="prefetch" href="/blogs/assets/js/339.057e28f8.js"><link rel="prefetch" href="/blogs/assets/js/34.eddf6f94.js"><link rel="prefetch" href="/blogs/assets/js/340.70ca722d.js"><link rel="prefetch" href="/blogs/assets/js/341.b83e3fc8.js"><link rel="prefetch" href="/blogs/assets/js/342.2276b167.js"><link rel="prefetch" href="/blogs/assets/js/343.ac09dccd.js"><link rel="prefetch" href="/blogs/assets/js/344.c80097fd.js"><link rel="prefetch" href="/blogs/assets/js/345.bb280ed6.js"><link rel="prefetch" href="/blogs/assets/js/346.5c1c9c8f.js"><link rel="prefetch" href="/blogs/assets/js/347.45f9adf8.js"><link rel="prefetch" href="/blogs/assets/js/348.55fa575c.js"><link rel="prefetch" href="/blogs/assets/js/349.14492112.js"><link rel="prefetch" href="/blogs/assets/js/35.2656429e.js"><link rel="prefetch" href="/blogs/assets/js/350.23760a3c.js"><link rel="prefetch" href="/blogs/assets/js/351.fbfa99b4.js"><link rel="prefetch" href="/blogs/assets/js/352.dd698868.js"><link rel="prefetch" href="/blogs/assets/js/353.40498626.js"><link rel="prefetch" href="/blogs/assets/js/354.588086f5.js"><link rel="prefetch" href="/blogs/assets/js/355.102fac11.js"><link rel="prefetch" href="/blogs/assets/js/356.cd1267bd.js"><link rel="prefetch" href="/blogs/assets/js/357.c65755fd.js"><link rel="prefetch" href="/blogs/assets/js/358.d415f76c.js"><link rel="prefetch" href="/blogs/assets/js/359.4530dbb7.js"><link rel="prefetch" href="/blogs/assets/js/36.07c0ff21.js"><link rel="prefetch" href="/blogs/assets/js/360.3f0f4e4c.js"><link rel="prefetch" href="/blogs/assets/js/361.850e34f5.js"><link rel="prefetch" href="/blogs/assets/js/362.a71e4238.js"><link rel="prefetch" href="/blogs/assets/js/363.f945f5ef.js"><link rel="prefetch" href="/blogs/assets/js/364.24e0c503.js"><link rel="prefetch" href="/blogs/assets/js/365.8312d12f.js"><link rel="prefetch" href="/blogs/assets/js/366.8d79aadc.js"><link rel="prefetch" href="/blogs/assets/js/367.80c580c9.js"><link rel="prefetch" href="/blogs/assets/js/368.767e89bb.js"><link rel="prefetch" href="/blogs/assets/js/369.455ee011.js"><link rel="prefetch" href="/blogs/assets/js/37.4a723830.js"><link rel="prefetch" href="/blogs/assets/js/370.77548a4b.js"><link rel="prefetch" href="/blogs/assets/js/371.fac90e21.js"><link rel="prefetch" href="/blogs/assets/js/372.5515dc63.js"><link rel="prefetch" href="/blogs/assets/js/373.50bec8c3.js"><link rel="prefetch" href="/blogs/assets/js/374.80fc0c40.js"><link rel="prefetch" href="/blogs/assets/js/375.5a1b8b5f.js"><link rel="prefetch" href="/blogs/assets/js/376.5f3782de.js"><link rel="prefetch" href="/blogs/assets/js/377.db20fdd4.js"><link rel="prefetch" href="/blogs/assets/js/378.c72b100b.js"><link rel="prefetch" href="/blogs/assets/js/379.d0c8d617.js"><link rel="prefetch" href="/blogs/assets/js/38.aa07c55d.js"><link rel="prefetch" href="/blogs/assets/js/380.7865f96f.js"><link rel="prefetch" href="/blogs/assets/js/381.6d80df08.js"><link rel="prefetch" href="/blogs/assets/js/382.054ff8d8.js"><link rel="prefetch" href="/blogs/assets/js/383.e4c6c136.js"><link rel="prefetch" href="/blogs/assets/js/384.301ca1c7.js"><link rel="prefetch" href="/blogs/assets/js/385.5511cf33.js"><link rel="prefetch" href="/blogs/assets/js/386.f9aa0fd5.js"><link rel="prefetch" href="/blogs/assets/js/387.5ae2eb60.js"><link rel="prefetch" href="/blogs/assets/js/388.1ecd0b93.js"><link rel="prefetch" href="/blogs/assets/js/389.e1585690.js"><link rel="prefetch" href="/blogs/assets/js/39.9d36a74f.js"><link rel="prefetch" href="/blogs/assets/js/390.2f1399c7.js"><link rel="prefetch" href="/blogs/assets/js/391.9b8b3ed6.js"><link rel="prefetch" href="/blogs/assets/js/392.0df64944.js"><link rel="prefetch" href="/blogs/assets/js/393.cd07056c.js"><link rel="prefetch" href="/blogs/assets/js/394.bfa9a9ff.js"><link rel="prefetch" href="/blogs/assets/js/395.0e837c6d.js"><link rel="prefetch" href="/blogs/assets/js/396.d0a581b4.js"><link rel="prefetch" href="/blogs/assets/js/397.da4377df.js"><link rel="prefetch" href="/blogs/assets/js/398.f71450a4.js"><link rel="prefetch" href="/blogs/assets/js/399.6284f2b9.js"><link rel="prefetch" href="/blogs/assets/js/4.b2084d38.js"><link rel="prefetch" href="/blogs/assets/js/40.2d10dee1.js"><link rel="prefetch" href="/blogs/assets/js/400.7040a50a.js"><link rel="prefetch" href="/blogs/assets/js/401.8bc0c0aa.js"><link rel="prefetch" href="/blogs/assets/js/402.013b720f.js"><link rel="prefetch" href="/blogs/assets/js/403.3e52abc2.js"><link rel="prefetch" href="/blogs/assets/js/404.d2cdd1ce.js"><link rel="prefetch" href="/blogs/assets/js/405.3e67b68c.js"><link rel="prefetch" href="/blogs/assets/js/406.79927ef5.js"><link rel="prefetch" href="/blogs/assets/js/407.b5ed71a7.js"><link rel="prefetch" href="/blogs/assets/js/408.145aa8f7.js"><link rel="prefetch" href="/blogs/assets/js/409.32610efe.js"><link rel="prefetch" href="/blogs/assets/js/41.21271d6a.js"><link rel="prefetch" href="/blogs/assets/js/410.4249dc3d.js"><link rel="prefetch" href="/blogs/assets/js/411.b31aa37d.js"><link rel="prefetch" href="/blogs/assets/js/412.03523ce7.js"><link rel="prefetch" href="/blogs/assets/js/413.e78807aa.js"><link rel="prefetch" href="/blogs/assets/js/414.a4a7aa8c.js"><link rel="prefetch" href="/blogs/assets/js/415.dfc662a7.js"><link rel="prefetch" href="/blogs/assets/js/416.d9ff6f56.js"><link rel="prefetch" href="/blogs/assets/js/417.c886ca90.js"><link rel="prefetch" href="/blogs/assets/js/418.58dbf064.js"><link rel="prefetch" href="/blogs/assets/js/419.21fbbd8f.js"><link rel="prefetch" href="/blogs/assets/js/42.2b169b47.js"><link rel="prefetch" href="/blogs/assets/js/420.692a166b.js"><link rel="prefetch" href="/blogs/assets/js/421.74f7624e.js"><link rel="prefetch" href="/blogs/assets/js/422.24b03dd4.js"><link rel="prefetch" href="/blogs/assets/js/423.84177236.js"><link rel="prefetch" href="/blogs/assets/js/424.a16c8c1f.js"><link rel="prefetch" href="/blogs/assets/js/425.e4790f48.js"><link rel="prefetch" href="/blogs/assets/js/426.76fb11ff.js"><link rel="prefetch" href="/blogs/assets/js/427.79f0988f.js"><link rel="prefetch" href="/blogs/assets/js/428.a7339a41.js"><link rel="prefetch" href="/blogs/assets/js/429.de6e129f.js"><link rel="prefetch" href="/blogs/assets/js/43.c565f1af.js"><link rel="prefetch" href="/blogs/assets/js/430.7fea3f78.js"><link rel="prefetch" href="/blogs/assets/js/431.dcc6f607.js"><link rel="prefetch" href="/blogs/assets/js/432.212162e9.js"><link rel="prefetch" href="/blogs/assets/js/433.f776b91d.js"><link rel="prefetch" href="/blogs/assets/js/434.3b707b56.js"><link rel="prefetch" href="/blogs/assets/js/435.b7aa4aee.js"><link rel="prefetch" href="/blogs/assets/js/436.80d22e5a.js"><link rel="prefetch" href="/blogs/assets/js/437.3053916e.js"><link rel="prefetch" href="/blogs/assets/js/438.35a1eb12.js"><link rel="prefetch" href="/blogs/assets/js/439.e1a159ba.js"><link rel="prefetch" href="/blogs/assets/js/44.162eb5da.js"><link rel="prefetch" href="/blogs/assets/js/440.eed32138.js"><link rel="prefetch" href="/blogs/assets/js/441.eac95432.js"><link rel="prefetch" href="/blogs/assets/js/442.4712abf5.js"><link rel="prefetch" href="/blogs/assets/js/443.4b5aaf02.js"><link rel="prefetch" href="/blogs/assets/js/444.909b694f.js"><link rel="prefetch" href="/blogs/assets/js/445.6a709980.js"><link rel="prefetch" href="/blogs/assets/js/446.e7b5d143.js"><link rel="prefetch" href="/blogs/assets/js/447.c2adf354.js"><link rel="prefetch" href="/blogs/assets/js/448.bc3639d5.js"><link rel="prefetch" href="/blogs/assets/js/449.de0efc63.js"><link rel="prefetch" href="/blogs/assets/js/45.273542f9.js"><link rel="prefetch" href="/blogs/assets/js/450.352c7201.js"><link rel="prefetch" href="/blogs/assets/js/451.548a0e77.js"><link rel="prefetch" href="/blogs/assets/js/452.cefaa4e7.js"><link rel="prefetch" href="/blogs/assets/js/453.699cb6b8.js"><link rel="prefetch" href="/blogs/assets/js/454.b7f7f39b.js"><link rel="prefetch" href="/blogs/assets/js/455.0aa0d132.js"><link rel="prefetch" href="/blogs/assets/js/456.752f987c.js"><link rel="prefetch" href="/blogs/assets/js/457.f2c21cb0.js"><link rel="prefetch" href="/blogs/assets/js/458.0fccfa1a.js"><link rel="prefetch" href="/blogs/assets/js/459.2b77e495.js"><link rel="prefetch" href="/blogs/assets/js/46.936a08c3.js"><link rel="prefetch" href="/blogs/assets/js/460.cc655724.js"><link rel="prefetch" href="/blogs/assets/js/461.5c9c96b2.js"><link rel="prefetch" href="/blogs/assets/js/462.12641aca.js"><link rel="prefetch" href="/blogs/assets/js/463.e9e3ea74.js"><link rel="prefetch" href="/blogs/assets/js/464.31126ee5.js"><link rel="prefetch" href="/blogs/assets/js/465.990c468c.js"><link rel="prefetch" href="/blogs/assets/js/466.958f9a54.js"><link rel="prefetch" href="/blogs/assets/js/467.228041f6.js"><link rel="prefetch" href="/blogs/assets/js/468.2fa13ad9.js"><link rel="prefetch" href="/blogs/assets/js/469.74b7d516.js"><link rel="prefetch" href="/blogs/assets/js/47.fc5aaa97.js"><link rel="prefetch" href="/blogs/assets/js/470.9c6ddcde.js"><link rel="prefetch" href="/blogs/assets/js/471.a68eabb2.js"><link rel="prefetch" href="/blogs/assets/js/472.98129924.js"><link rel="prefetch" href="/blogs/assets/js/473.17794672.js"><link rel="prefetch" href="/blogs/assets/js/474.2995095f.js"><link rel="prefetch" href="/blogs/assets/js/475.64f80f36.js"><link rel="prefetch" href="/blogs/assets/js/48.895827c2.js"><link rel="prefetch" href="/blogs/assets/js/49.2cecf465.js"><link rel="prefetch" href="/blogs/assets/js/5.d7bbd3d9.js"><link rel="prefetch" href="/blogs/assets/js/50.2972ddee.js"><link rel="prefetch" href="/blogs/assets/js/51.eab84d6d.js"><link rel="prefetch" href="/blogs/assets/js/52.8a915401.js"><link rel="prefetch" href="/blogs/assets/js/53.8eabe307.js"><link rel="prefetch" href="/blogs/assets/js/54.dd0207a2.js"><link rel="prefetch" href="/blogs/assets/js/55.9071c1f8.js"><link rel="prefetch" href="/blogs/assets/js/56.abf569a1.js"><link rel="prefetch" href="/blogs/assets/js/57.55b7a067.js"><link rel="prefetch" href="/blogs/assets/js/58.41f9b620.js"><link rel="prefetch" href="/blogs/assets/js/59.6ebf6261.js"><link rel="prefetch" href="/blogs/assets/js/6.ee65c1e7.js"><link rel="prefetch" href="/blogs/assets/js/60.869205cc.js"><link rel="prefetch" href="/blogs/assets/js/61.2cb25722.js"><link rel="prefetch" href="/blogs/assets/js/62.07a76d23.js"><link rel="prefetch" href="/blogs/assets/js/63.06b6fdce.js"><link rel="prefetch" href="/blogs/assets/js/64.6d213f4c.js"><link rel="prefetch" href="/blogs/assets/js/65.205a4511.js"><link rel="prefetch" href="/blogs/assets/js/66.aaaffa89.js"><link rel="prefetch" href="/blogs/assets/js/67.1d6a2e56.js"><link rel="prefetch" href="/blogs/assets/js/68.3a3a1937.js"><link rel="prefetch" href="/blogs/assets/js/69.233e765f.js"><link rel="prefetch" href="/blogs/assets/js/7.95c5731c.js"><link rel="prefetch" href="/blogs/assets/js/70.581b4adb.js"><link rel="prefetch" href="/blogs/assets/js/71.c5412492.js"><link rel="prefetch" href="/blogs/assets/js/72.20ef83ef.js"><link rel="prefetch" href="/blogs/assets/js/73.2bb1c525.js"><link rel="prefetch" href="/blogs/assets/js/74.1516c569.js"><link rel="prefetch" href="/blogs/assets/js/75.c3370786.js"><link rel="prefetch" href="/blogs/assets/js/76.80e4fe0c.js"><link rel="prefetch" href="/blogs/assets/js/77.f2c7adac.js"><link rel="prefetch" href="/blogs/assets/js/78.8e806f46.js"><link rel="prefetch" href="/blogs/assets/js/79.18d03739.js"><link rel="prefetch" href="/blogs/assets/js/8.6c8082e2.js"><link rel="prefetch" href="/blogs/assets/js/80.a65a8759.js"><link rel="prefetch" href="/blogs/assets/js/81.d8a5fbe0.js"><link rel="prefetch" href="/blogs/assets/js/82.c642a64d.js"><link rel="prefetch" href="/blogs/assets/js/83.33fa18d7.js"><link rel="prefetch" href="/blogs/assets/js/84.f6edf7b8.js"><link rel="prefetch" href="/blogs/assets/js/85.b2751517.js"><link rel="prefetch" href="/blogs/assets/js/86.3c361927.js"><link rel="prefetch" href="/blogs/assets/js/87.1af5df70.js"><link rel="prefetch" href="/blogs/assets/js/88.ec0bd5de.js"><link rel="prefetch" href="/blogs/assets/js/89.aa8e0f1e.js"><link rel="prefetch" href="/blogs/assets/js/9.b33bc7c6.js"><link rel="prefetch" href="/blogs/assets/js/90.dba5a8d2.js"><link rel="prefetch" href="/blogs/assets/js/91.fb84fe0a.js"><link rel="prefetch" href="/blogs/assets/js/92.92e6527e.js"><link rel="prefetch" href="/blogs/assets/js/93.a9cede4e.js"><link rel="prefetch" href="/blogs/assets/js/94.e2e01b5e.js"><link rel="prefetch" href="/blogs/assets/js/95.5ca324a4.js"><link rel="prefetch" href="/blogs/assets/js/96.d70070fb.js"><link rel="prefetch" href="/blogs/assets/js/97.84d7ab42.js"><link rel="prefetch" href="/blogs/assets/js/98.a33985b1.js"><link rel="prefetch" href="/blogs/assets/js/99.bbe78d52.js">
    <link rel="stylesheet" href="/blogs/assets/css/0.styles.7a0dfb7e.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container have-rightmenu" data-v-f848d4e8><div class="global-loading-wrapper" data-v-3a334c6d data-v-f848d4e8 data-v-f848d4e8><div class="loader-main" data-v-3a334c6d><img src="/blogs/assets/img/loading.a592e2e5.jpg" alt="loading" data-v-3a334c6d></div></div> <div class="hide" data-v-f848d4e8><header class="navbar" data-v-f848d4e8><div title="目录" class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/blogs/" class="home-link router-link-active"><img src="/blogs/img/kmc.jpg" alt="卡夫卡的岛上书店" class="logo"> <span class="site-name">卡夫卡的岛上书店</span></a> <div class="links"><div class="color-picker"><a class="color-button"><i class="iconfont reco-color"></i></a> <div class="color-picker-menu" style="display:none;"><div class="mode-options"><h4 class="title">选择模式</h4> <ul class="color-mode-options"><li class="dark">深色模式</li><li class="light active">浅色模式</li><li class="read">阅读模式</li></ul></div></div></div> <div class="search-box"><i class="iconfont reco-search"></i> <input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <ul class="suggestions" style="display:none;"></ul></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/blogs/" class="nav-link"><i class="iconfont reco-home"></i>首页
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><a href="/blogs/web/" class="nav-link"><i></i>前端
      </a></span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>文档教程</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/web/docs/imooc/wiki/" class="nav-link"><i class="iconfont reco-blog"></i>慕课教程
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/es6/" class="nav-link"><i class="iconfont reco-blog"></i>ES6 入门教程
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/wangdoc/javascript/" class="nav-link"><i class="iconfont reco-blog"></i>网道-JavaScript 教程
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/angular/" class="nav-link"><i class="iconfont reco-blog"></i>Angular 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/react/" class="nav-link"><i class="iconfont reco-blog"></i>React 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/vue/" class="nav-link"><i class="iconfont reco-blog"></i>Vue 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/node/" class="nav-link"><i class="iconfont reco-blog"></i>Node 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/babel/" class="nav-link"><i class="iconfont reco-blog"></i>Babel 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/webpack/" class="nav-link"><i class="iconfont reco-blog"></i>Webpack 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/typescript/" class="nav-link"><i class="iconfont reco-blog"></i>TypeScript 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/runoob/" class="nav-link"><i class="iconfont reco-blog"></i>菜鸟教程
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/docschina/" class="nav-link"><i class="iconfont reco-blog"></i>印记中文
</a></li></ul></li><li class="dropdown-item"><h4>专栏笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/column-notes/33-js/" class="nav-link"><i class="iconfont reco-blog"></i>JavaScript的33个概念
</a></li><li class="dropdown-subitem"><a href="/blogs/column-notes/relearn-frontend/" class="nav-link"><i class="iconfont reco-blog"></i>重学前端
</a></li><li class="dropdown-subitem"><a href="/blogs/column-notes/http-protocol/" class="nav-link"><i class="iconfont reco-blog"></i>透视HTTP协议
</a></li><li class="dropdown-subitem"><a href="/blogs/column-notes/play-webpack/" class="nav-link"><i class="iconfont reco-blog"></i>玩转webpack
</a></li><li class="dropdown-subitem"><a href="/blogs/column-notes/browser-principle/" class="nav-link"><i class="iconfont reco-blog"></i>浏览器工作原理与实践
</a></li></ul></li><li class="dropdown-item"><h4>书籍笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/book-notes/donot-know-javascript/" class="nav-link"><i class="iconfont reco-blog"></i>你不知道的JavaScript
</a></li></ul></li><li class="dropdown-item"><h4>推荐博客</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/web/ruanyifeng/" class="nav-link"><i class="iconfont reco-blog"></i>阮一峰的个人网站
</a></li><li class="dropdown-subitem"><a href="/blogs/web/liaoxuefeng/" class="nav-link"><i class="iconfont reco-blog"></i>廖雪峰的官方网站
</a></li></ul></li><li class="dropdown-item"><h4>工具</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/web/tools/astexplorer/" class="nav-link"><i class="iconfont reco-blog"></i>AST Explorer
</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><a href="/blogs/golang/" class="nav-link router-link-active"><i></i>Golang
      </a></span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>文档资源</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/golang/docs/gopl-zh/" class="nav-link"><i class="iconfont reco-blog"></i>Go语言圣经（中文版）
</a></li><li class="dropdown-subitem"><a href="/blogs/golang/docs/31de972b403fd/" class="nav-link"><i class="iconfont reco-blog"></i>Go 语言学习资料
</a></li></ul></li><li class="dropdown-item"><h4>书籍笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/golang/book-notes/the-way-to-go/" class="nav-link router-link-active"><i class="iconfont reco-blog"></i>Go入门指南
</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><a href="/blogs/library/" class="nav-link"><i></i>图书馆
      </a></span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>太易</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/map/" class="nav-link"><i class="iconfont reco-blog"></i>时间地图
</a></li></ul></li><li class="dropdown-item"><h4>太初</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/wiki/" class="nav-link"><i class="iconfont reco-blog"></i>中文维基百科
</a></li></ul></li><li class="dropdown-item"><h4>太始</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/wdl/" class="nav-link"><i class="iconfont reco-blog"></i>世界数字图书馆
</a></li></ul></li><li class="dropdown-item"><h4>太素</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/douban/" class="nav-link"><i class="iconfont reco-blog"></i>豆瓣榜单
</a></li><li class="dropdown-subitem"><a href="/blogs/library/open163/" class="nav-link"><i class="iconfont reco-blog"></i>网易公开课
</a></li></ul></li><li class="dropdown-item"><h4>太极</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/shici/" class="nav-link"><i class="iconfont reco-blog"></i>中华诗词
</a></li><li class="dropdown-subitem"><a href="/blogs/library/processon/" class="nav-link"><i class="iconfont reco-blog"></i>ProcessOn图形化知识资源
</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><a href="/blogs/idealism/" class="nav-link"><i></i>昨日与明日
      </a></span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>昨日</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/idealism/music/" class="nav-link"><i class="iconfont reco-blog"></i>音乐
</a></li></ul></li><li class="dropdown-item"><h4>今日</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/idealism/philosophy/" class="nav-link"><i class="iconfont reco-blog"></i>哲学
</a></li></ul></li><li class="dropdown-item"><h4>明日</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/idealism/kaimo/" class="nav-link"><i class="iconfont reco-blog"></i>忧伤的年轻人
</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-api"></i>索引
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blogs/sitemap/" class="nav-link"><i class="iconfont reco-blog"></i>总目录
</a></li><li class="dropdown-item"><!----> <a href="/blogs/categories/" class="nav-link"><i class="iconfont reco-category"></i>分类
</a></li><li class="dropdown-item"><!----> <a href="/blogs/tags/" class="nav-link"><i class="iconfont reco-tag"></i>标签
</a></li><li class="dropdown-item"><!----> <a href="/blogs/archives/" class="nav-link"><i class="iconfont reco-date"></i>归档
</a></li></ul></div></div><div class="nav-item"><a href="/blogs/bookshop/message-board/" class="nav-link"><i class="iconfont reco-suggestion"></i>留言板
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-message"></i>关于
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>联系</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="https://blog.csdn.net/kaimo313" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-csdn"></i>CSDN
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-subitem"><a href="https://gitee.com/kaimo313" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-mayun"></i>Gitee
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-subitem"><a href="https://github.com/kaimo313" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-github"></i>GitHub
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-subitem"><a href="/blogs/bookshop/linkme/" class="nav-link"><i class="iconfont reco-account"></i>联系店长
</a></li></ul></li><li class="dropdown-item"><h4>博客</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/bookshop/explain/" class="nav-link"><i class="iconfont reco-document"></i>博客说明
</a></li><li class="dropdown-subitem"><a href="/blogs/bookshop/logs/" class="nav-link"><i class="iconfont reco-document"></i>更新日志
</a></li></ul></li><li class="dropdown-item"><h4>其他</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/resource-tools/" class="nav-link"><i class="iconfont reco-document"></i>资源工具
</a></li><li class="dropdown-subitem"><a href="/blogs/bookshop/friendslink/" class="nav-link"><i class="iconfont reco-friend"></i>友情链接
</a></li></ul></li></ul></div></div> <!----></nav></div></header> <div class="sidebar-mask" data-v-f848d4e8></div> <aside class="sidebar" data-v-f848d4e8><div class="personal-info-wrapper" data-v-34faaed8 data-v-f848d4e8><img src="/blogs/img/avatar.jpg" alt="author-avatar" class="personal-img" data-v-34faaed8> <div class="author" data-v-34faaed8>
    凯小默
  </div> <div class="personal-info-details" data-v-34faaed8><div data-v-34faaed8>文章：<span data-v-34faaed8>404</span></div> <div data-v-34faaed8>地点：<span data-v-34faaed8>canton</span></div></div> <div class="slogan" data-v-34faaed8>
    standing on the shoulders of giants
  </div> <ul class="social-links" data-v-34faaed8><li class="social-item" data-v-34faaed8><i class="iconfont reco-csdn" style="color:#fc5531;" data-v-34faaed8></i></li><li class="social-item" data-v-34faaed8><i class="iconfont reco-mayun" style="color:#c71d24;" data-v-34faaed8></i></li><li class="social-item" data-v-34faaed8><i class="iconfont reco-bilibili" style="color:#fb7299;" data-v-34faaed8></i></li><li class="social-item" data-v-34faaed8><i class="iconfont reco-github" style="color:#3eaf7c;" data-v-34faaed8></i></li><li class="social-item" data-v-34faaed8><i class="iconfont reco-mail" style="color:#00a1d6;" data-v-34faaed8></i></li></ul></div> <nav class="nav-links"><div class="nav-item"><a href="/blogs/" class="nav-link"><i class="iconfont reco-home"></i>首页
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><a href="/blogs/web/" class="nav-link"><i></i>前端
      </a></span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>文档教程</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/web/docs/imooc/wiki/" class="nav-link"><i class="iconfont reco-blog"></i>慕课教程
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/es6/" class="nav-link"><i class="iconfont reco-blog"></i>ES6 入门教程
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/wangdoc/javascript/" class="nav-link"><i class="iconfont reco-blog"></i>网道-JavaScript 教程
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/angular/" class="nav-link"><i class="iconfont reco-blog"></i>Angular 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/react/" class="nav-link"><i class="iconfont reco-blog"></i>React 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/vue/" class="nav-link"><i class="iconfont reco-blog"></i>Vue 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/node/" class="nav-link"><i class="iconfont reco-blog"></i>Node 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/babel/" class="nav-link"><i class="iconfont reco-blog"></i>Babel 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/webpack/" class="nav-link"><i class="iconfont reco-blog"></i>Webpack 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/typescript/" class="nav-link"><i class="iconfont reco-blog"></i>TypeScript 文档
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/runoob/" class="nav-link"><i class="iconfont reco-blog"></i>菜鸟教程
</a></li><li class="dropdown-subitem"><a href="/blogs/web/docs/docschina/" class="nav-link"><i class="iconfont reco-blog"></i>印记中文
</a></li></ul></li><li class="dropdown-item"><h4>专栏笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/column-notes/33-js/" class="nav-link"><i class="iconfont reco-blog"></i>JavaScript的33个概念
</a></li><li class="dropdown-subitem"><a href="/blogs/column-notes/relearn-frontend/" class="nav-link"><i class="iconfont reco-blog"></i>重学前端
</a></li><li class="dropdown-subitem"><a href="/blogs/column-notes/http-protocol/" class="nav-link"><i class="iconfont reco-blog"></i>透视HTTP协议
</a></li><li class="dropdown-subitem"><a href="/blogs/column-notes/play-webpack/" class="nav-link"><i class="iconfont reco-blog"></i>玩转webpack
</a></li><li class="dropdown-subitem"><a href="/blogs/column-notes/browser-principle/" class="nav-link"><i class="iconfont reco-blog"></i>浏览器工作原理与实践
</a></li></ul></li><li class="dropdown-item"><h4>书籍笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/book-notes/donot-know-javascript/" class="nav-link"><i class="iconfont reco-blog"></i>你不知道的JavaScript
</a></li></ul></li><li class="dropdown-item"><h4>推荐博客</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/web/ruanyifeng/" class="nav-link"><i class="iconfont reco-blog"></i>阮一峰的个人网站
</a></li><li class="dropdown-subitem"><a href="/blogs/web/liaoxuefeng/" class="nav-link"><i class="iconfont reco-blog"></i>廖雪峰的官方网站
</a></li></ul></li><li class="dropdown-item"><h4>工具</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/web/tools/astexplorer/" class="nav-link"><i class="iconfont reco-blog"></i>AST Explorer
</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><a href="/blogs/golang/" class="nav-link router-link-active"><i></i>Golang
      </a></span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>文档资源</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/golang/docs/gopl-zh/" class="nav-link"><i class="iconfont reco-blog"></i>Go语言圣经（中文版）
</a></li><li class="dropdown-subitem"><a href="/blogs/golang/docs/31de972b403fd/" class="nav-link"><i class="iconfont reco-blog"></i>Go 语言学习资料
</a></li></ul></li><li class="dropdown-item"><h4>书籍笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/golang/book-notes/the-way-to-go/" class="nav-link router-link-active"><i class="iconfont reco-blog"></i>Go入门指南
</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><a href="/blogs/library/" class="nav-link"><i></i>图书馆
      </a></span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>太易</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/map/" class="nav-link"><i class="iconfont reco-blog"></i>时间地图
</a></li></ul></li><li class="dropdown-item"><h4>太初</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/wiki/" class="nav-link"><i class="iconfont reco-blog"></i>中文维基百科
</a></li></ul></li><li class="dropdown-item"><h4>太始</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/wdl/" class="nav-link"><i class="iconfont reco-blog"></i>世界数字图书馆
</a></li></ul></li><li class="dropdown-item"><h4>太素</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/douban/" class="nav-link"><i class="iconfont reco-blog"></i>豆瓣榜单
</a></li><li class="dropdown-subitem"><a href="/blogs/library/open163/" class="nav-link"><i class="iconfont reco-blog"></i>网易公开课
</a></li></ul></li><li class="dropdown-item"><h4>太极</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/library/shici/" class="nav-link"><i class="iconfont reco-blog"></i>中华诗词
</a></li><li class="dropdown-subitem"><a href="/blogs/library/processon/" class="nav-link"><i class="iconfont reco-blog"></i>ProcessOn图形化知识资源
</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><a href="/blogs/idealism/" class="nav-link"><i></i>昨日与明日
      </a></span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>昨日</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/idealism/music/" class="nav-link"><i class="iconfont reco-blog"></i>音乐
</a></li></ul></li><li class="dropdown-item"><h4>今日</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/idealism/philosophy/" class="nav-link"><i class="iconfont reco-blog"></i>哲学
</a></li></ul></li><li class="dropdown-item"><h4>明日</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/idealism/kaimo/" class="nav-link"><i class="iconfont reco-blog"></i>忧伤的年轻人
</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-api"></i>索引
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blogs/sitemap/" class="nav-link"><i class="iconfont reco-blog"></i>总目录
</a></li><li class="dropdown-item"><!----> <a href="/blogs/categories/" class="nav-link"><i class="iconfont reco-category"></i>分类
</a></li><li class="dropdown-item"><!----> <a href="/blogs/tags/" class="nav-link"><i class="iconfont reco-tag"></i>标签
</a></li><li class="dropdown-item"><!----> <a href="/blogs/archives/" class="nav-link"><i class="iconfont reco-date"></i>归档
</a></li></ul></div></div><div class="nav-item"><a href="/blogs/bookshop/message-board/" class="nav-link"><i class="iconfont reco-suggestion"></i>留言板
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-message"></i>关于
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>联系</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="https://blog.csdn.net/kaimo313" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-csdn"></i>CSDN
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-subitem"><a href="https://gitee.com/kaimo313" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-mayun"></i>Gitee
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-subitem"><a href="https://github.com/kaimo313" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-github"></i>GitHub
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-subitem"><a href="/blogs/bookshop/linkme/" class="nav-link"><i class="iconfont reco-account"></i>联系店长
</a></li></ul></li><li class="dropdown-item"><h4>博客</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/bookshop/explain/" class="nav-link"><i class="iconfont reco-document"></i>博客说明
</a></li><li class="dropdown-subitem"><a href="/blogs/bookshop/logs/" class="nav-link"><i class="iconfont reco-document"></i>更新日志
</a></li></ul></li><li class="dropdown-item"><h4>其他</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blogs/resource-tools/" class="nav-link"><i class="iconfont reco-document"></i>资源工具
</a></li><li class="dropdown-subitem"><a href="/blogs/bookshop/friendslink/" class="nav-link"><i class="iconfont reco-friend"></i>友情链接
</a></li></ul></li></ul></div></div> <!----></nav> <ul class="sidebar-links"><li><a href="/blogs/golang/book-notes/the-way-to-go/af321cdfef51/" class="sidebar-link">前言</a></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>第一部分：学习 Go 语言</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>第二部分：语言的核心结构与技术</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>第三部分：Go 高级编程</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>第四部分：实际应用</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <div><main class="page"><div class="theme-vdoing-wrapper bg-style-12"><div class="articleInfo-wrap" data-v-13f19dad><div class="articleInfo" data-v-13f19dad><ul class="breadcrumbs" data-v-13f19dad><li data-v-13f19dad><a href="/blogs/" title="首页" class="fa fa-laptop-house router-link-active" data-v-13f19dad></a></li> <li data-v-13f19dad><a href="/blogs/golang/book-notes/the-way-to-go/" title="Go入门指南-目录页" class="router-link-active" data-v-13f19dad>Go入门指南</a></li> <li data-v-13f19dad><a href="/blogs/golang/book-notes/the-way-to-go/#第三部分：Go 高级编程" title="Go入门指南#第三部分：Go 高级编程" data-v-13f19dad>第三部分：Go 高级编程</a></li> <li data-v-13f19dad><a href="/blogs/golang/book-notes/the-way-to-go/#第14章：协程（goroutine）与通道（channel）" title="Go入门指南#第14章：协程（goroutine）与通道（channel）" data-v-13f19dad>第14章：协程（goroutine）与通道（channel）</a></li></ul> <div class="info" data-v-13f19dad><div title="作者" class="author fa fa-user" data-v-13f19dad><a href="https://github.com/unknwon/" target="_blank" title="作者" class="beLink" data-v-13f19dad>unknwon</a></div> <div title="创建时间" class="date fa fa-calendar-alt" data-v-13f19dad><a href="javascript:;" data-v-13f19dad>2021/03/05 17:33:01</a></div> <!----></div></div></div> <!----> <div class="content-wrapper"><div class="right-menu-wrapper"><div class="right-menu-margin"><div class="right-menu-content"></div></div></div> <h1><img src="">
            协程间的信道
          </h1> <div class="theme-vdoing-content content__default"><h2 id="_14-2-1-概念"><a href="#_14-2-1-概念" class="header-anchor">#</a> 14.2.1 概念</h2> <p>在第一个例子中，协程是独立执行的，他们之间没有通信。他们必须通信才会变得更有用：彼此之间发送和接收信息并且协调/同步他们的工作。协程可以使用共享变量来通信，但是很不提倡这样做，因为这种方式给所有的共享内存的多线程都带来了困难。</p> <p>而 Go 有一种特殊的类型，<em>通道（channel）</em>，就像一个可以用于发送类型化数据的管道，由其负责协程之间的通信，从而避开所有由共享内存导致的陷阱；这种通过通道进行通信的方式保证了同步性。数据在通道中进行传递：<em>在任何给定时间，一个数据被设计为只有一个协程可以对其访问，所以不会发生数据竞争。</em> 数据的所有权（可以读写数据的能力）也因此被传递。</p> <p>工厂的传送带是个很有用的例子。一个机器（生产者协程）在传送带上放置物品，另外一个机器（消费者协程）拿到物品并打包。</p> <p>通道服务于通信的两个目的：值的交换，同步的，保证了两个计算（协程）任何时候都是可知状态。</p> <p><img src="/blogs/assets/img/14.2_fig14.1.21c4452a.png" alt=""></p> <p>通常使用这样的格式来声明通道：<code>var identifier chan datatype</code></p> <p>未初始化的通道的值是nil。</p> <p>所以通道只能传输一种类型的数据，比如 <code>chan int</code> 或者 <code>chan string</code>，所有的类型都可以用于通道，空接口 <code>interface{}</code> 也可以。甚至可以（有时非常有用）创建通道的通道。</p> <p>通道实际上是类型化消息的队列：使数据得以传输。它是先进先出（FIFO）的结构所以可以保证发送给他们的元素的顺序（有些人知道，通道可以比作 Unix shells 中的双向管道（two-way pipe））。通道也是引用类型，所以我们使用 <code>make()</code> 函数来给它分配内存。这里先声明了一个字符串通道 ch1，然后创建了它（实例化）：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">var</span> ch1 <span class="token keyword">chan</span> <span class="token builtin">string</span>
ch1 <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>当然可以更短： <code>ch1 := make(chan string)</code>。</p> <p>这里我们构建一个int通道的通道： <code>chanOfChans := make(chan int)</code>。</p> <p>或者函数通道：<code>funcChan := make(chan func())</code>（相关示例请看第 <a href="/blogs/GO01.Go入门指南/04.第三部分：Go 高级编程/03.第14章：协程（goroutine）与通道（channel）/14.17.html">14.17</a> 节）。</p> <p>所以通道是第一类对象：可以存储在变量中，作为函数的参数传递，从函数返回以及通过通道发送它们自身。另外它们是类型化的，允许类型检查，比如尝试使用整数通道发送一个指针。</p> <h2 id="_14-2-2-通信操作符"><a href="#_14-2-2-通信操作符" class="header-anchor">#</a> 14.2.2 通信操作符 &lt;-</h2> <p>这个操作符直观的标示了数据的传输：信息按照箭头的方向流动。</p> <p>流向通道（发送）</p> <p><code>ch &lt;- int1</code> 表示：用通道 ch 发送变量 int1（双目运算符，中缀 = 发送）</p> <p>从通道流出（接收），三种方式：</p> <p><code>int2 = &lt;- ch</code> 表示：变量 int2 从通道 ch（一元运算的前缀操作符，前缀 = 接收）接收数据（获取新值）；假设 int2 已经声明过了，如果没有的话可以写成：<code>int2 := &lt;- ch</code>。</p> <p><code>&lt;- ch</code> 可以单独调用获取通道的（下一个）值，当前值会被丢弃，但是可以用来验证，所以以下代码是合法的：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">if</span> <span class="token operator">&lt;-</span> ch <span class="token operator">!=</span> <span class="token number">1000</span><span class="token punctuation">{</span>
	<span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>同一个操作符 <code>&lt;-</code> 既用于<strong>发送</strong>也用于<strong>接收</strong>，但Go会根据操作对象弄明白该干什么 。虽非强制要求，但为了可读性通道的命名通常以 <code>ch</code> 开头或者包含 <code>chan</code>。通道的发送和接收都是原子操作：它们总是互不干扰的完成的。下面的示例展示了通信操作符的使用。</p> <p>示例 14.2-<a href="examples/chapter_14/goroutine2.go">goroutine2.go</a></p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span>

	<span class="token keyword">go</span> <span class="token function">sendData</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token function">getData</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span>

	time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1e9</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">sendData</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	ch <span class="token operator">&lt;-</span> <span class="token string">&quot;Washington&quot;</span>
	ch <span class="token operator">&lt;-</span> <span class="token string">&quot;Tripoli&quot;</span>
	ch <span class="token operator">&lt;-</span> <span class="token string">&quot;London&quot;</span>
	ch <span class="token operator">&lt;-</span> <span class="token string">&quot;Beijing&quot;</span>
	ch <span class="token operator">&lt;-</span> <span class="token string">&quot;Tokyo&quot;</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">getData</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">var</span> input <span class="token builtin">string</span>
	<span class="token comment">// time.Sleep(2e9)</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		input <span class="token operator">=</span> <span class="token operator">&lt;-</span>ch
		fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">&quot;%s &quot;</span><span class="token punctuation">,</span> input<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br></div></div><p>输出：</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>Washington Tripoli London Beijing tokyo
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p><code>main()</code> 函数中启动了两个协程：<code>sendData()</code> 通过通道 ch 发送了 5 个字符串，<code>getData()</code> 按顺序接收它们并打印出来。</p> <p>如果 2 个协程需要通信，你必须给他们同一个通道作为参数才行。</p> <p>尝试一下如果注释掉 <code>time.Sleep(1e9)</code> 会如何。</p> <p>我们发现协程之间的同步非常重要：</p> <ul><li>main() 等待了 1 秒让两个协程完成，如果不这样，sendData() 就没有机会输出。</li> <li>getData() 使用了无限循环：它随着 sendData() 的发送完成和 ch 变空也结束了。</li> <li>如果我们移除一个或所有 <code>go</code> 关键字，程序无法运行，Go 运行时会抛出 panic：</li></ul> <div class="language- line-numbers-mode"><pre class="language-text"><code>---- Error run E:/Go/Goboek/code examples/chapter 14/goroutine2.exe with code Crashed ---- Program exited with code -2147483645: panic: all goroutines are asleep-deadlock!
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>为什么会这样？运行时（runtime）会检查所有的协程（像本例中只有一个）是否在等待着什么东西（可从某个通道读取或者写入某个通道），这意味着程序将无法继续执行。这是死锁（deadlock）的一种形式，而运行时（runtime）可以为我们检测到这种情况。</p> <p>注意：不要使用打印状态来表明通道的发送和接收顺序：由于打印状态和通道实际发生读写的时间延迟会导致和真实发生的顺序不同。</p> <p>练习 14.4：解释一下为什么如果在函数 <code>getData()</code> 的一开始插入 <code>time.Sleep(2e9)</code>，不会出现错误但也没有输出呢。</p> <h2 id="_14-2-3-通道阻塞"><a href="#_14-2-3-通道阻塞" class="header-anchor">#</a> 14.2.3 通道阻塞</h2> <p>默认情况下，通信是同步且无缓冲的：在有接受者接收数据之前，发送不会结束。可以想象一个无缓冲的通道在没有空间来保存数据的时候：必须要一个接收者准备好接收通道的数据然后发送者可以直接把数据发送给接收者。所以通道的发送/接收操作在对方准备好之前是阻塞的：</p> <p>1）对于同一个通道，发送操作（协程或者函数中的），在接收者准备好之前是阻塞的：如果ch中的数据无人接收，就无法再给通道传入其他数据：新的输入无法在通道非空的情况下传入。所以发送操作会等待 ch 再次变为可用状态：就是通道值被接收时（可以传入变量）。</p> <p>2）对于同一个通道，接收操作是阻塞的（协程或函数中的），直到发送者可用：如果通道中没有数据，接收者就阻塞了。</p> <p>尽管这看上去是非常严格的约束，实际在大部分情况下工作的很不错。</p> <p>程序 <code>channel_block.go</code> 验证了以上理论，一个协程在无限循环中给通道发送整数数据。不过因为没有接收者，只输出了一个数字 0。</p> <p>示例 14.3-<a href="examples/chapter_14/channel_block.go">channel_block.go</a></p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token string">&quot;fmt&quot;</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	ch1 <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token function">pump</span><span class="token punctuation">(</span>ch1<span class="token punctuation">)</span>       <span class="token comment">// pump hangs</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator">&lt;-</span>ch1<span class="token punctuation">)</span> <span class="token comment">// prints only 0</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">pump</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		ch <span class="token operator">&lt;-</span> i
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>输出：</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>0
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p><code>pump()</code> 函数为通道提供数值，也被叫做生产者。</p> <p>为通道解除阻塞定义了 <code>suck</code> 函数来在无限循环中读取通道，参见示例 14.4-<a href="examples/chapter_14/channel_block2.go">channel_block2.go</a>：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">func</span> <span class="token function">suck</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator">&lt;-</span>ch<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>在 <code>main()</code> 中使用协程开始它：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">go</span> <span class="token function">pump</span><span class="token punctuation">(</span>ch1<span class="token punctuation">)</span>
<span class="token keyword">go</span> <span class="token function">suck</span><span class="token punctuation">(</span>ch1<span class="token punctuation">)</span>
time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1e9</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>给程序 1 秒的时间来运行：输出了上万个整数。</p> <p>练习 14.1：<a href="exercises/chapter_14/channel_block3.go">channel_block3.go</a>：写一个通道证明它的阻塞性，开启一个协程接收通道的数据，持续 15 秒，然后给通道放入一个值。在不同的阶段打印消息并观察输出。</p> <h2 id="_14-2-4-通过一个-或多个-通道交换数据进行协程同步。"><a href="#_14-2-4-通过一个-或多个-通道交换数据进行协程同步。" class="header-anchor">#</a> 14.2.4 通过一个（或多个）通道交换数据进行协程同步。</h2> <p>通信是一种同步形式：通过通道，两个协程在通信（协程会和）中某刻同步交换数据。无缓冲通道成为了多个协程同步的完美工具。</p> <p>甚至可以在通道两端互相阻塞对方，形成了叫做死锁的状态。Go 运行时会检查并 panic，停止程序。死锁几乎完全是由糟糕的设计导致的。</p> <p>无缓冲通道会被阻塞。设计无阻塞的程序可以避免这种情况，或者使用带缓冲的通道。</p> <p>练习 14.2： <a href="exercises/chapter_14/blocking.go">blocking.go</a></p> <p>解释为什么下边这个程序会导致 panic：所有的协程都休眠了 - 死锁！</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">f1</span><span class="token punctuation">(</span>in <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator">&lt;-</span>in<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	out <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
	out <span class="token operator">&lt;-</span> <span class="token number">2</span>
	<span class="token keyword">go</span> <span class="token function">f1</span><span class="token punctuation">(</span>out<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><h2 id="_14-2-5-同步通道-使用带缓冲的通道"><a href="#_14-2-5-同步通道-使用带缓冲的通道" class="header-anchor">#</a> 14.2.5 同步通道-使用带缓冲的通道</h2> <p>一个无缓冲通道只能包含 1 个元素，有时显得很局限。我们给通道提供了一个缓存，可以在扩展的 <code>make</code> 命令中设置它的容量，如下：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>buf <span class="token operator">:=</span> <span class="token number">100</span>
ch1 <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">,</span> buf<span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>buf 是通道可以同时容纳的元素（这里是 string）个数</p> <p>在缓冲满载（缓冲被全部使用）之前，给一个带缓冲的通道发送数据是不会阻塞的，而从通道读取数据也不会阻塞，直到缓冲空了。</p> <p>缓冲容量和类型无关，所以可以（尽管可能导致危险）给一些通道设置不同的容量，只要他们拥有同样的元素类型。内置的 <code>cap</code> 函数可以返回缓冲区的容量。</p> <p>如果容量大于 0，通道就是异步的了：缓冲满载（发送）或变空（接收）之前通信不会阻塞，元素会按照发送的顺序被接收。如果容量是0或者未设置，通信仅在收发双方准备好的情况下才可以成功。</p> <p>同步：<code>ch :=make(chan type, value)</code></p> <ul><li>value == 0 -&gt; synchronous, unbuffered (阻塞）</li> <li>value &gt; 0 -&gt; asynchronous, buffered（非阻塞）取决于value元素</li></ul> <p>若使用通道的缓冲，你的程序会在“请求”激增的时候表现更好：更具弹性，专业术语叫：更具有伸缩性（scalable）。在设计算法时首先考虑使用无缓冲通道，只在不确定的情况下使用缓冲。</p> <p>练习 14.3：<a href="exercises/chapter_14/channel_buffer.go">channel_buffer.go</a>：给 <a href="exercises/chapter_14/channel_block3.go">channel_block3.go</a> 的通道增加缓冲并观察输出有何不同。</p> <h2 id="_14-2-6-协程中用通道输出结果"><a href="#_14-2-6-协程中用通道输出结果" class="header-anchor">#</a> 14.2.6 协程中用通道输出结果</h2> <p>为了知道计算何时完成，可以通过信道回报。在例子 <code>go sum(bigArray)</code> 中，要这样写：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
<span class="token keyword">go</span> <span class="token function">sum</span><span class="token punctuation">(</span>bigArray<span class="token punctuation">,</span> ch<span class="token punctuation">)</span> <span class="token comment">// bigArray puts the calculated sum on ch</span>
<span class="token comment">// .. do something else for a while</span>
sum <span class="token operator">:=</span> <span class="token operator">&lt;-</span> ch <span class="token comment">// wait for, and retrieve the sum</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>也可以使用通道来达到同步的目的，这个很有效的用法在传统计算机中称为信号量（semaphore）。或者换个方式：通过通道发送信号告知处理已经完成（在协程中）。</p> <p>在其他协程运行时让 main 程序无限阻塞的通常做法是在 <code>main</code> 函数的最后放置一个 <code>select {}</code>。</p> <p>也可以使用通道让 <code>main</code> 程序等待协程完成，就是所谓的信号量模式，我们会在接下来的部分讨论。</p> <h2 id="_14-2-7-信号量模式"><a href="#_14-2-7-信号量模式" class="header-anchor">#</a> 14.2.7 信号量模式</h2> <p>下边的片段阐明：协程通过在通道 <code>ch</code> 中放置一个值来处理结束的信号。<code>main</code> 协程等待 <code>&lt;-ch</code> 直到从中获取到值。</p> <p>我们期望从这个通道中获取返回的结果，像这样：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">func</span> <span class="token function">compute</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
	ch <span class="token operator">&lt;-</span> <span class="token function">someComputation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// when it completes, signal on the channel.</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> 	<span class="token comment">// allocate a channel.</span>
	<span class="token keyword">go</span> <span class="token function">compute</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span>		<span class="token comment">// start something in a goroutines</span>
	<span class="token function">doSomethingElseForAWhile</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	result <span class="token operator">:=</span> <span class="token operator">&lt;-</span> ch
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>这个信号也可以是其他的，不返回结果，比如下面这个协程中的匿名函数（lambda）协程：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
	<span class="token comment">// doSomething</span>
	ch <span class="token operator">&lt;-</span> <span class="token number">1</span> <span class="token comment">// Send a signal; value does not matter</span>
<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token function">doSomethingElseForAWhile</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token operator">&lt;-</span> ch	<span class="token comment">// Wait for goroutine to finish; discard sent value.</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>或者等待两个协程完成，每一个都会对切片s的一部分进行排序，片段如下：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>done <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">bool</span><span class="token punctuation">)</span>
<span class="token comment">// doSort is a lambda function, so a closure which knows the channel done:</span>
doSort <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>s <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
	<span class="token function">sort</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span>
	done <span class="token operator">&lt;-</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span>
i <span class="token operator">:=</span> <span class="token function">pivot</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span>
<span class="token keyword">go</span> <span class="token function">doSort</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span><span class="token punctuation">:</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token keyword">go</span> <span class="token function">doSort</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">:</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token operator">&lt;-</span>done
<span class="token operator">&lt;-</span>done
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>下边的代码，用完整的信号量模式对长度为N的 float64 切片进行了 N 个<code>doSomething()</code> 计算并同时完成，通道 sem 分配了相同的长度（且包含空接口类型的元素），待所有的计算都完成后，发送信号（通过放入值）。在循环中从通道 sem 不停的接收数据来等待所有的协程完成。</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">type</span> Empty <span class="token keyword">interface</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">var</span> empty Empty
<span class="token operator">...</span>
data <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">float64</span><span class="token punctuation">,</span> N<span class="token punctuation">)</span>
res <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">float64</span><span class="token punctuation">,</span> N<span class="token punctuation">)</span>
sem <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> Empty<span class="token punctuation">,</span> N<span class="token punctuation">)</span>
<span class="token operator">...</span>
<span class="token keyword">for</span> i<span class="token punctuation">,</span> xi <span class="token operator">:=</span> <span class="token keyword">range</span> data <span class="token punctuation">{</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span> <span class="token punctuation">(</span>i <span class="token builtin">int</span><span class="token punctuation">,</span> xi <span class="token builtin">float64</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		res<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">doSomething</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> xi<span class="token punctuation">)</span>
		sem <span class="token operator">&lt;-</span> empty
	<span class="token punctuation">}</span> <span class="token punctuation">(</span>i<span class="token punctuation">,</span> xi<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token comment">// wait for goroutines to finish</span>
<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> N<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span> <span class="token operator">&lt;-</span>sem <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>注意上述代码中闭合函数的用法：<code>i</code>、<code>xi</code> 都是作为参数传入闭合函数的，这一做法使得每个协程（译者注：在其启动时）获得一份 <code>i</code> 和 <code>xi</code> 的单独拷贝，从而向闭合函数内部屏蔽了外层循环中的 <code>i</code> 和 <code>xi</code>变量；否则，for 循环的下一次迭代会更新所有协程中 <code>i</code> 和 <code>xi</code> 的值。另一方面，切片 <code>res</code> 没有传入闭合函数，因为协程不需要<code>res</code>的单独拷贝。切片 <code>res</code> 也在闭合函数中但并不是参数。</p> <h2 id="_14-2-8-实现并行的-for-循环"><a href="#_14-2-8-实现并行的-for-循环" class="header-anchor">#</a> 14.2.8 实现并行的 for 循环</h2> <p>在上一部分章节 <a href="/blogs/GO01.Go入门指南/04.第三部分：Go 高级编程/03.第14章：协程（goroutine）与通道（channel）/14.2.html#1427-信号量模式">14.2.7</a> 的代码片段中：for 循环的每一个迭代是并行完成的：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">for</span> i<span class="token punctuation">,</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> data <span class="token punctuation">{</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span> <span class="token punctuation">(</span>i <span class="token builtin">int</span><span class="token punctuation">,</span> v <span class="token builtin">float64</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token function">doSomething</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> v<span class="token punctuation">)</span>
		<span class="token operator">...</span>
	<span class="token punctuation">}</span> <span class="token punctuation">(</span>i<span class="token punctuation">,</span> v<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>在 for 循环中并行计算迭代可能带来很好的性能提升。不过所有的迭代都必须是独立完成的。有些语言比如 Fortress 或者其他并行框架以不同的结构实现了这种方式，在 Go 中用协程实现起来非常容易：</p> <h2 id="_14-2-9-用带缓冲通道实现一个信号量"><a href="#_14-2-9-用带缓冲通道实现一个信号量" class="header-anchor">#</a> 14.2.9 用带缓冲通道实现一个信号量</h2> <p>信号量是实现互斥锁（排外锁）常见的同步机制，限制对资源的访问，解决读写问题，比如没有实现信号量的 <code>sync</code> 的 Go 包，使用带缓冲的通道可以轻松实现：</p> <ul><li>带缓冲通道的容量和要同步的资源容量相同</li> <li>通道的长度（当前存放的元素个数）与当前资源被使用的数量相同</li> <li>容量减去通道的长度就是未处理的资源个数（标准信号量的整数值）</li></ul> <p>不用管通道中存放的是什么，只关注长度；因此我们创建了一个长度可变但容量为0（字节）的通道：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">type</span> Empty <span class="token keyword">interface</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">type</span> semaphore <span class="token keyword">chan</span> Empty
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>将可用资源的数量N来初始化信号量 <code>semaphore</code>：<code>sem = make(semaphore, N)</code></p> <p>然后直接对信号量进行操作：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token comment">// acquire n resources</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>s semaphore<span class="token punctuation">)</span> <span class="token function">P</span><span class="token punctuation">(</span>n <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	e <span class="token operator">:=</span> <span class="token function">new</span><span class="token punctuation">(</span>Empty<span class="token punctuation">)</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> n<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		s <span class="token operator">&lt;-</span> e
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// release n resources</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>s semaphore<span class="token punctuation">)</span> <span class="token function">V</span><span class="token punctuation">(</span>n <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> i<span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> n<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">{</span>
		<span class="token operator">&lt;-</span> s
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><p>可以用来实现一个互斥的例子：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token comment">/* mutexes */</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>s semaphore<span class="token punctuation">)</span> <span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	s<span class="token punctuation">.</span><span class="token function">P</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>s semaphore<span class="token punctuation">)</span> <span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
	s<span class="token punctuation">.</span><span class="token function">V</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token comment">/* signal-wait */</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>s semaphore<span class="token punctuation">)</span> <span class="token function">Wait</span><span class="token punctuation">(</span>n <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	s<span class="token punctuation">.</span><span class="token function">P</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>s semaphore<span class="token punctuation">)</span> <span class="token function">Signal</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	s<span class="token punctuation">.</span><span class="token function">V</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br></div></div><p>练习 14.5：<a href="exercises/chapter_14/gosum.go">gosum.go</a>：用这种习惯用法写一个程序，开启一个协程来计算2个整数的和并等待计算结果并打印出来。</p> <p>练习 14.6：<a href="exercises/chapter_14/producer_consumer.go">producer_consumer.go</a>：用这种习惯用法写一个程序，有两个协程，第一个提供数字 0，10，20，...90 并将他们放入通道，第二个协程从通道中读取并打印。<code>main()</code> 等待两个协程完成后再结束。</p> <p>习惯用法：通道工厂模式</p> <p>编程中常见的另外一种模式如下：不将通道作为参数传递给协程，而用函数来生成一个通道并返回（工厂角色）；函数内有个匿名函数被协程调用。</p> <p>在 <a href="examples/chapter_14/channel_block2.go">channel_block2.go</a> 加入这种模式便有了示例 14.5-<a href="examples/chapter_14/channel_idiom.go">channel_idiom.go</a>：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	stream <span class="token operator">:=</span> <span class="token function">pump</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token function">suck</span><span class="token punctuation">(</span>stream<span class="token punctuation">)</span>
	time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1e9</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">pump</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
			ch <span class="token operator">&lt;-</span> i
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">return</span> ch
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">suck</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator">&lt;-</span>ch<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br></div></div><h2 id="_14-2-10-给通道使用-for-循环"><a href="#_14-2-10-给通道使用-for-循环" class="header-anchor">#</a> 14.2.10 给通道使用 for 循环</h2> <p><code>for</code> 循环的 <code>range</code> 语句可以用在通道 <code>ch</code> 上，便可以从通道中获取值，像这样：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">for</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> ch <span class="token punctuation">{</span>
	fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">&quot;The value is %v\n&quot;</span><span class="token punctuation">,</span> v<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>它从指定通道中读取数据直到通道关闭，才继续执行下边的代码。很明显，另外一个协程必须写入 <code>ch</code>（不然代码就阻塞在 for 循环了），而且必须在写入完成后才关闭。<code>suck</code> 函数可以这样写，且在协程中调用这个动作，程序变成了这样：</p> <p>示例 14.6-<a href="examples/chapter_14/channel_idiom2.go">channel_idiom2.go</a>：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token function">suck</span><span class="token punctuation">(</span><span class="token function">pump</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
	time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1e9</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">pump</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
			ch <span class="token operator">&lt;-</span> i
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">return</span> ch
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">suck</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token keyword">for</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> ch <span class="token punctuation">{</span>
			fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br></div></div><p>习惯用法：通道迭代模式</p> <p>这个模式用到了后边14.6章示例 <a href="exercises/chapter_14/producer_consumer.go">producer_consumer.go</a> 的生产者-消费者模式，通常，需要从包含了地址索引字段 items 的容器给通道填入元素。为容器的类型定义一个方法 <code>Iter()</code>，返回一个只读的通道（参见第 <a href="/blogs/GO01.Go入门指南/04.第三部分：Go 高级编程/03.第14章：协程（goroutine）与通道（channel）/14.2.html#14211-通道的方向">14.2.11</a> 节）items，如下：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">func</span> <span class="token punctuation">(</span>c <span class="token operator">*</span>container<span class="token punctuation">)</span> Iter <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;-</span> <span class="token keyword">chan</span> item <span class="token punctuation">{</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> item<span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token keyword">for</span> i<span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> c<span class="token punctuation">.</span><span class="token function">Len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">{</span>	<span class="token comment">// or use a for-range loop</span>
			ch <span class="token operator">&lt;-</span> c<span class="token punctuation">.</span>items<span class="token punctuation">[</span>i<span class="token punctuation">]</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span> <span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">return</span> ch
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>在协程里，一个 for 循环迭代容器 c 中的元素（对于树或图的算法，这种简单的 for 循环可以替换为深度优先搜索）。</p> <p>调用这个方法的代码可以这样迭代容器：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">for</span> x <span class="token operator">:=</span> <span class="token keyword">range</span> container<span class="token punctuation">.</span><span class="token function">Iter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>其运行在自己启动的协程中，所以上边的迭代用到了一个通道和两个协程（可能运行在不同的线程上）。 这样我们就有了一个典型的生产者-消费者模式。如果在程序结束之前，向通道写值的协程未完成工作，则这个协程不会被垃圾回收；这是设计使然。这种看起来并不符合预期的行为正是由通道这种线程安全的通信方式所导致的。如此一来，一个协程为了写入一个永远无人读取的通道而被挂起就成了一个bug，而并非你预想中的那样被悄悄回收掉（garbage-collected）了。</p> <p>习惯用法：生产者消费者模式</p> <p>假设你有 <code>Produce()</code> 函数来产生 <code>Consume</code> 函数需要的值。它们都可以运行在独立的协程中，生产者在通道中放入给消费者读取的值。整个处理过程可以替换为无限循环：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">for</span> <span class="token punctuation">{</span>
	<span class="token function">Consume</span><span class="token punctuation">(</span><span class="token function">Produce</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><h2 id="_14-2-11-通道的方向"><a href="#_14-2-11-通道的方向" class="header-anchor">#</a> 14.2.11 通道的方向</h2> <p>通道类型可以用注解来表示它只发送或者只接收：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">var</span> send_only <span class="token keyword">chan</span><span class="token operator">&lt;-</span> <span class="token builtin">int</span> 		<span class="token comment">// channel can only receive data</span>
<span class="token keyword">var</span> recv_only <span class="token operator">&lt;-</span><span class="token keyword">chan</span> <span class="token builtin">int</span>		<span class="token comment">// channel can only send data</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>只接收的通道（&lt;-chan T）无法关闭，因为关闭通道是发送者用来表示不再给通道发送值了，所以对只接收通道是没有意义的。通道创建的时候都是双向的，但也可以分配有方向的通道变量，就像以下代码：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">var</span> c <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token comment">// bidirectional</span>
<span class="token keyword">go</span> <span class="token function">source</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span>
<span class="token keyword">go</span> <span class="token function">sink</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">source</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span><span class="token operator">&lt;-</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span> ch <span class="token operator">&lt;-</span> <span class="token number">1</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">sink</span><span class="token punctuation">(</span>ch <span class="token operator">&lt;-</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span> <span class="token operator">&lt;-</span>ch <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>习惯用法：管道和选择器模式</p> <p>更具体的例子还有协程处理它从通道接收的数据并发送给输出通道：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>sendChan <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
receiveChan <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span>
<span class="token keyword">go</span> <span class="token function">processChannel</span><span class="token punctuation">(</span>sendChan<span class="token punctuation">,</span> receiveChan<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">processChannel</span><span class="token punctuation">(</span>in <span class="token operator">&lt;-</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> out <span class="token keyword">chan</span><span class="token operator">&lt;-</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> inValue <span class="token operator">:=</span> <span class="token keyword">range</span> in <span class="token punctuation">{</span>
		result <span class="token operator">:=</span> <span class="token operator">...</span> <span class="token comment">/// processing inValue</span>
		out <span class="token operator">&lt;-</span> result
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>通过使用方向注解来限制协程对通道的操作。</p> <p>这里有一个来自 Go 指导的很赞的例子，打印了输出的素数，使用选择器（‘筛’）作为它的算法。每个 prime 都有一个选择器，如下图：</p> <p><img src="/blogs/assets/img/14.2_fig14.2.6df84086.png" alt=""></p> <p>版本1：示例 14.7-<a href="examples/chapter_14/sieve1.go">sieve1.go</a></p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token comment">// Copyright 2009 The Go Authors. All rights reserved.</span>
<span class="token comment">// Use of this source code is governed by a BSD-style</span>
<span class="token comment">// license that can be found in the LICENSE file.package main</span>
<span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token string">&quot;fmt&quot;</span>

<span class="token comment">// Send the sequence 2, 3, 4, ... to channel 'ch'.</span>
<span class="token keyword">func</span> <span class="token function">generate</span><span class="token punctuation">(</span>ch <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		ch <span class="token operator">&lt;-</span> i <span class="token comment">// Send 'i' to channel 'ch'.</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// Copy the values from channel 'in' to channel 'out',</span>
<span class="token comment">// removing those divisible by 'prime'.</span>
<span class="token keyword">func</span> <span class="token function">filter</span><span class="token punctuation">(</span>in<span class="token punctuation">,</span> out <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> prime <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		i <span class="token operator">:=</span> <span class="token operator">&lt;-</span>in <span class="token comment">// Receive value of new variable 'i' from 'in'.</span>
		<span class="token keyword">if</span> i<span class="token operator">%</span>prime <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span>
			out <span class="token operator">&lt;-</span> i <span class="token comment">// Send 'i' to channel 'out'.</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// The prime sieve: Daisy-chain filter processes together.</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token comment">// Create a new channel.</span>
	<span class="token keyword">go</span> <span class="token function">generate</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span>      <span class="token comment">// Start generate() as a goroutine.</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		prime <span class="token operator">:=</span> <span class="token operator">&lt;-</span>ch
		fmt<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>prime<span class="token punctuation">,</span> <span class="token string">&quot; &quot;</span><span class="token punctuation">)</span>
		ch1 <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
		<span class="token keyword">go</span> <span class="token function">filter</span><span class="token punctuation">(</span>ch<span class="token punctuation">,</span> ch1<span class="token punctuation">,</span> prime<span class="token punctuation">)</span>
		ch <span class="token operator">=</span> ch1
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br></div></div><p>协程 <code>filter(in, out chan int, prime int)</code> 拷贝整数到输出通道，丢弃掉可以被 prime 整除的数字。然后每个 prime 又开启了一个新的协程，生成器和选择器并发请求。</p> <p>输出：</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101
103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223
227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349
353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479
487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619
631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769
773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929
937 941 947 953 967 971 977 983 991 997 1009 1013...
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>第二个版本引入了上边的习惯用法：函数 <code>sieve</code>、<code>generate</code> 和 <code>filter</code> 都是工厂；它们创建通道并返回，而且使用了协程的 lambda 函数。<code>main</code> 函数现在短小清晰：它调用 <code>sieve()</code> 返回了包含素数的通道，然后通过 <code>fmt.Println(&lt;-primes)</code> 打印出来。</p> <p>版本2：示例 14.8-<a href="examples/chapter_14/sieve2.go">sieve2.go</a></p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token comment">// Copyright 2009 The Go Authors. All rights reserved.</span>
<span class="token comment">// Use of this source code is governed by a BSD-style</span>
<span class="token comment">// license that can be found in the LICENSE file.</span>

<span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
<span class="token punctuation">)</span>

<span class="token comment">// Send the sequence 2, 3, 4, ... to returned channel</span>
<span class="token keyword">func</span> <span class="token function">generate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
			ch <span class="token operator">&lt;-</span> i
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">return</span> ch
<span class="token punctuation">}</span>

<span class="token comment">// Filter out input values divisible by 'prime', send rest to returned channel</span>
<span class="token keyword">func</span> <span class="token function">filter</span><span class="token punctuation">(</span>in <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> prime <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>
	out <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token keyword">for</span> <span class="token punctuation">{</span>
			<span class="token keyword">if</span> i <span class="token operator">:=</span> <span class="token operator">&lt;-</span>in<span class="token punctuation">;</span> i<span class="token operator">%</span>prime <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span>
				out <span class="token operator">&lt;-</span> i
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">return</span> out
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">sieve</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>
	out <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		ch <span class="token operator">:=</span> <span class="token function">generate</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
		<span class="token keyword">for</span> <span class="token punctuation">{</span>
			prime <span class="token operator">:=</span> <span class="token operator">&lt;-</span>ch
			ch <span class="token operator">=</span> <span class="token function">filter</span><span class="token punctuation">(</span>ch<span class="token punctuation">,</span> prime<span class="token punctuation">)</span>
			out <span class="token operator">&lt;-</span> prime
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">return</span> out
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	primes <span class="token operator">:=</span> <span class="token function">sieve</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator">&lt;-</span>primes<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br></div></div></div></div> <div class="page-edit"><!----> <div class="last-updated"><span class="prefix">最近更新时间：</span> <span class="time">2021/03/16 19:48:24</span></div></div> <div class="page-nav-wapper"><div class="page-nav-centre-wrap"><a href="/blogs/golang/book-notes/the-way-to-go/a63d15c4c1903/" class="page-nav-centre page-nav-centre-prev"><div class="tooltip">并发、并行和协程</div></a> <a href="/blogs/golang/book-notes/the-way-to-go/f26e5ecaf17ae/" class="page-nav-centre page-nav-centre-next"><div class="tooltip">协程的同步：关闭通道-测试阻塞的通道</div></a></div> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/blogs/golang/book-notes/the-way-to-go/a63d15c4c1903/" class="prev">并发、并行和协程</a></span> <span class="next"><a href="/blogs/golang/book-notes/the-way-to-go/f26e5ecaf17ae/">协程的同步：关闭通道-测试阻塞的通道</a>→
      </span></p></div></div></div> <div class="theme-vdoing-wrapper article-list bg-style-14"><div class="article-title"><a href="/blogs/archives/" class="fa fa-pencil-alt">
      最近更新
    </a></div> <div class="article-wrapper"><dl><dd>01</dd> <dt><div><a href="/blogs/golang/book-notes/the-way-to-go/b451871c7ebf3/"><div>总结和增强</div></a></div> <div class="date"><span>2021/03/16 19:29:10</span></div></dt></dl><dl><dd>02</dd> <dt><div><a href="/blogs/golang/book-notes/the-way-to-go/af1d6a84a8f0c/"><div>使用代理缓存</div></a></div> <div class="date"><span>2021/03/16 19:28:46</span></div></dt></dl><dl><dd>03</dd> <dt><div><a href="/blogs/golang/book-notes/the-way-to-go/49b0f32997236/"><div>版本 5 - 分布式程序</div></a></div> <div class="date"><span>2021/03/16 19:28:23</span></div></dt></dl> <div class="more-box"><a href="/blogs/archives/" class="more"><i class="fas fa-angle-double-right"></i>前往更多 ...
      </a></div></div></div> <div class="comments-wrapper" style="display:none;"><!----></div></main></div> <div class="footer-wrapper" data-v-ae53833e data-v-ae53833e><span class="footer-reco-theme" data-v-ae53833e><i class="iconfont reco-theme" data-v-ae53833e></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-ae53833e>vuepress-theme-reco@1.6.1</a> <a target="blank" href="https://doc.xugaoyi.com/" class="vdoing ml5" data-v-ae53833e>vuepress-theme-vdoing@1.7.2</a></span> <!----> <span class="footer-copyright" data-v-ae53833e><i class="iconfont reco-copyright" data-v-ae53833e></i> <a data-v-ae53833e><span data-v-ae53833e>凯小默</span> <span class="ml5" data-v-ae53833e>
          2019 - 2021
        </span></a></span> <span class="footer-view-site" data-v-ae53833e><i class="iconfont reco-eye" data-v-ae53833e></i> <span id="busuanzi_container_site_uv" data-v-ae53833e>
        访客数：<span id="busuanzi_value_site_uv" class="num" data-v-ae53833e>-</span></span> <span id="busuanzi_container_site_pv" class="ml5" data-v-ae53833e>
        访问量：<span id="busuanzi_value_site_pv" class="num" data-v-ae53833e>-</span></span></span> <!----></div> <!----></div></div><div class="global-ui"><div class="back-to-ceiling" style="right:1rem;bottom:6rem;width:2.5rem;height:2.5rem;border-radius:.25rem;line-height:2.5rem;display:none;" data-v-c6073ba8 data-v-c6073ba8><svg t="1574745035067" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5404" class="icon" data-v-c6073ba8><path d="M526.60727968 10.90185116a27.675 27.675 0 0 0-29.21455937 0c-131.36607665 82.28402758-218.69155461 228.01873535-218.69155402 394.07834331a462.20625001 462.20625001 0 0 0 5.36959153 69.94390903c1.00431239 6.55289093-0.34802892 13.13561351-3.76865779 18.80351572-32.63518765 54.11355614-51.75690182 118.55860487-51.7569018 187.94566865a371.06718723 371.06718723 0 0 0 11.50484808 91.98906777c6.53300375 25.50556257 41.68394495 28.14064038 52.69160883 4.22606766 17.37162448-37.73630017 42.14135425-72.50938081 72.80769204-103.21549295 2.18761121 3.04276886 4.15646224 6.24463696 6.40373557 9.22774369a1871.4375 1871.4375 0 0 0 140.04691725 5.34970492 1866.36093723 1866.36093723 0 0 0 140.04691723-5.34970492c2.24727335-2.98310674 4.21612437-6.18497483 6.3937923-9.2178004 30.66633723 30.70611158 55.4360664 65.4791928 72.80769147 103.21549355 11.00766384 23.91457269 46.15860503 21.27949489 52.69160879-4.22606768a371.15156223 371.15156223 0 0 0 11.514792-91.99901164c0-69.36717486-19.13165746-133.82216804-51.75690182-187.92578088-3.42062944-5.66790279-4.76302748-12.26056868-3.76865837-18.80351632a462.20625001 462.20625001 0 0 0 5.36959269-69.943909c-0.00994388-166.08943902-87.32547796-311.81420293-218.6915546-394.09823051zM605.93803103 357.87693858a93.93749974 93.93749974 0 1 1-187.89594924 6.1e-7 93.93749974 93.93749974 0 0 1 187.89594924-6.1e-7z" p-id="5405" data-v-c6073ba8></path><path d="M429.50777625 765.63860547C429.50777625 803.39355007 466.44236686 1000.39046097 512.00932183 1000.39046097c45.56695499 0 82.4922232-197.00623328 82.5015456-234.7518555 0-37.75494459-36.9345906-68.35043303-82.4922232-68.34111062-45.57627738-0.00932239-82.52019037 30.59548842-82.51086798 68.34111062z" p-id="5406" data-v-c6073ba8></path></svg></div><!----><div></div><APlayer audio="" fixed="true" mini="true" theme="#3eaf7c" loop="loop" order="random" preload="auto" volume="1" mutex="true" lrc-type="0" list-folded="true" list-max-height="250" storage-name="vuepress-plugin-meting" id="aplayer-fixed"></APlayer></div></div>
    <script src="/blogs/assets/js/app.b6577c57.js" defer></script><script src="/blogs/assets/js/2.44f167e6.js" defer></script><script src="/blogs/assets/js/17.59a993c4.js" defer></script><script src="/blogs/assets/js/10.3a18c1d1.js" defer></script>
  </body>
</html>
